<!DOCTYPE html>
<html>
<head>
  <script src="/resources/testharness.js"></script>
  <script src="/resources/testharnessreport.js"></script>
  <script src="/resources/testdriver.js"></script>
  <script src="/resources/testdriver-actions.js"></script>
  <script src="/resources/testdriver-vendor.js"></script>
  <script src="/dom/events/scrolling/scroll_support.js"></script>
  <script src="/css/css-scroll-snap-2/resources/common.js"></script>
  <script src="/css/css-scroll-snap-2/resources/user-scroll-common.js"></script>
  <script src="/web-animations/testcommon.js"></script>
  <style>
    body {
      margin: 0px;
    }
    #space {
      height: 200vh;
      width: 200vw;
    }
    .scroller {
        scroll-snap-type: x mandatory;
        overflow-x: auto;
        overflow-y: hidden;
        position: relative;
        height: 500px;
        width: 500px;
      }

      .box {
        scroll-snap-align: start;
        height: 100px;
        width: 100px;
        position: absolute;
        top: 200px;
      }

      #box1 {
        background-color: red;
      }

      #box2 {
        background-color: yellow;
        left: 200px;
      }

      #box3 {
        background-color: blue;
        left: 400px;
      }
  </style>
</head>
<body>
  <div id="scroller" class="scroller">
    <div id="space"></div>
    <div id="box1" class="box"><h1>1</h1></div>
    <div id="box2" class="box"><h1>2</h1></div>
    <div id="box3" class="box"><h1>3</h1></div>
  </div>
  <script>
    const scroller = document.getElementById("scroller");
    promise_test(async (t) => {
      // This tests scrollsnapchanging firing after a layout change in the middle of a
      // touch scroll. We start a touch scroll far enough that scrollsnapchanging
      // fires and then, with the pointer still down, we change the layout so
      // that scrollsnapchanging should fire with a different target.
      await waitForScrollReset(t, scroller);
      await waitForCompositorCommit();

      const start_pos_x = Math.round(box2.offsetLeft);
      // Drag by enough to ensure box2 is the preferred snap target.
      const drag_amt =  Math.round(box2.offsetLeft / 2) + 50;
      const end_pos_x = start_pos_x - drag_amt;
      const pos_y = Math.round(scroller.clientHeight / 2);
      let evt_promise;
      let snap_evt;

      const save_scrollsnapchanging_evt = (evt) => { snap_evt = evt; }
      evt_promise = scroller.addEventListener("scrollsnapchanging",
                                              save_scrollsnapchanging_evt);
      // We wait to reach the expected scroll position rather than waiting for a
      // scrollsnapchanging event to avoid timing out if the scrollsnapchanging event does
      // not fire.
      const scroll_promise = new Promise((resolve) => {
        scroller.addEventListener("scroll", async () => {
          if (scroller.scrollLeft >= (box2.offsetLeft / 2)) {
            await waitForAnimationFrames(2);
            resolve();
          }
        });
      });

      await new test_driver.Actions()
        .addPointer("TestPointer", "touch")
        .pointerMove(start_pos_x, pos_y)
        .pointerDown()
        .addTick()
        .pause(200)
        // Drag closer to box2, which should trigger a scrollsnapchanging event.
        .pointerMove(start_pos_x - drag_amt, pos_y)
        .send();

      // assert scrollsnapchanging that should have already happened.
      await scroll_promise;
      assertSnapEvent(snap_evt, { block: null, inline: box2 });

      evt_promise = waitForSnapEvent(scroller, "scrollsnapchanging", false);
      // Change layout while pointer is still down.
      let box2_prev_left = getComputedStyle(box2).getPropertyValue("left");
      let box3_prev_left = getComputedStyle(box3).getPropertyValue("left");
      box2.style.left = box3_prev_left;
      box3.style.left = box2_prev_left;
      snap_evt = await evt_promise;
      assertSnapEvent(snap_evt, { block: null, inline: box3 });
    }, "scrollsnapchanging fires after layout change");
  </script>
</body>
</html>